package com.sky.service.impl;


import com.sky.entity.Orders;
import com.sky.mapper.ReportMapper;
import com.sky.service.ReportService;
import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.LongStream;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {
    @Autowired
    private ReportMapper reportMapper;


    /**
     * 统计用户新增人数与总数
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
        //1、通过初试时间和截止时间获取区间
        List<LocalDate> localDates =this.getTemporalInterval(begin,end);
        //2、拿到区间时间内每天的新增人数
        //2.1、用Map封装方法参数
        List<Map> userNumMapList =  this.reportMapper.groupAddUserNum(begin,end);
        //3、拿到初始时间前一天的总人数
        Map map = new HashMap();
        map.put("begin", begin.minusDays(1));
        map.put("userFlag", true);
        Integer count =  this.reportMapper.addUserCount(map);
        //4、遍历区间集合，将每天的新增人数放入集合
        //4.1 创建返回容器;
        List<Long> newUserList = new ArrayList<>();
        List<Integer> totalUserList = new ArrayList<>();
        //4.2 将userNumMapList 转换为Map
        Map<String, Long> userNumMap = userNumMapList.stream()
                .collect(Collectors.toMap(u -> (String) u.get("date"), u -> (Long) u.get("num")));
        for (LocalDate date : localDates) {
            Long num = userNumMap.get(date.toString());
            //4.3 如果num 为空，则给赋值0
            num = num == null ? 0:num;
            //4.4 新增用户
            newUserList.add(num);
            //4.5 用户总数
            count += num.intValue();
            totalUserList.add(count);
        }
        //4.1、用总人数累加结果放入集合
        return UserReportVO.builder().dateList(StringUtils.join(localDates,",")).newUserList(StringUtils.join(newUserList,",")).totalUserList(StringUtils.join(totalUserList,",")).build();
    }

    /**
     * 营业额数据统计
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public TurnoverReportVO getTurnover(LocalDate begin, LocalDate end) {
        //1、根据初始时间和结束时间获时间区间集合
        List<LocalDate> localDates = this.getTemporalInterval(begin, end);
        //2、分组查询区间内每天所有收入金额
        Map map = new HashMap();
        map.put("begin",begin);
        map.put("end",end);
        map.put("status", Orders.COMPLETED);
        List<Map> numMapList = this.reportMapper.selectOrderNumbers(map);
        //3、创建集合容器，按时间顺序将收入金额存入集合
        //3.1 将集合转Map
        Map<String, BigDecimal> numMap = numMapList.stream().collect(Collectors.toMap(n -> (String) n.get("date"), n -> (BigDecimal) n.get("num")));
        List<BigDecimal> turnoverList = new ArrayList<>();
        //3.2 遍历时间集合
        for (LocalDate date : localDates) {
            BigDecimal num = numMap.get(date.toString());
            num = num != null ?num : BigDecimal.valueOf(0);
            turnoverList.add(num);
        }
        //4、将集合转为逗号分隔字符串返
        return TurnoverReportVO.builder().
                dateList(StringUtils.join(localDates,","))
                .turnoverList(StringUtils.join(turnoverList,","))
                .build();
    }

    /**
     * 用户数据统计
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {
        //1、根据初始时间和结束时间获时间区间集合
        List<LocalDate> localDates = this.getTemporalInterval(begin, end);
        //2、分组查询区间内每日订单数和日有效订单数
        Map map = new HashMap();
        map.put("begin",begin);
        map.put("end",end);
        //2.1 有效订单
        map.put("status",Orders.COMPLETED);
        List<Map> validOrderCountList = this.reportMapper.getOrdersCount(map);
        //2.2 所有订单
        map.put("status",null);
        List<Map> orderCountList = this.reportMapper.getOrdersCount(map);
        //2.3 将集合转为Map
        Map<String, Long> validOrderMap = validOrderCountList.stream().collect(Collectors.toMap(n -> (String) n.get("date"), n -> (Long) n.get("num")));
        Map<String, Long> OrderMap = orderCountList.stream().collect(Collectors.toMap(n -> (String) n.get("date"), n -> (Long) n.get("num")));

        //3、创建集合容器，按时间顺序将订单存入集合
        List<Long> validOrderCounts = new ArrayList<>();
        List<Long> orderCountLists = new ArrayList<>();
        //3.1遍历时间集合
        for (LocalDate date : localDates) {
            //有效订单
            Long validOrder = validOrderMap.get(date.toString());
            validOrder = validOrder != null ? validOrder : 0;
            validOrderCounts.add(validOrder);
            //所有订单
            Long Order = OrderMap.get(date.toString());
            Order = Order != null ? Order : 0;
            orderCountLists.add(Order);
        }
        //4、计算订单总数，有效订单数，订单完成率
        Long totalOrderCount = orderCountLists.stream().flatMapToLong(o -> LongStream.of(o)).sum();
        //有效订单数
        Long validOrderCount = validOrderCounts.stream().flatMapToLong(o -> LongStream.of(o)).sum();
        //订单完成率
        //4.1 避免分母为零

        Double orderCompletionRate =totalOrderCount == 0 ? 0.0 : (double) validOrderCount / totalOrderCount;

        //5、将集合转为逗号分隔字符串返
        return OrderReportVO.builder()
                .dateList(StringUtils.join(localDates,","))
                .orderCompletionRate(orderCompletionRate)
                .orderCountList(StringUtils.join(orderCountLists,","))
                .totalOrderCount(Math.toIntExact(totalOrderCount))
                .validOrderCount(Math.toIntExact(validOrderCount))
                .validOrderCountList(StringUtils.join(validOrderCounts,","))
                .build();
    }

    /**
     * 销量排名统计
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end) {
        //1、分组查询区间内销量最高的菜品并倒序排列
        Map map = new HashMap();
        map.put("begin",begin);
        map.put("end",end);
        List<Map> dishMapList = this.reportMapper.selectDishSalesTop10(map);
        //2、创建集合容器，按销量顺序将菜品转换为逗号分隔的字符串
        //将集合转Map
        List<String> nameList  = dishMapList.stream().map(dish -> (String)dish.get("name")).collect(Collectors.toList());
        List<BigDecimal> numberList = dishMapList.stream().map(dish -> (BigDecimal)dish.get("num")).collect(Collectors.toList());
        return SalesTop10ReportVO.builder()
                .nameList(StringUtils.join(nameList,","))
                .numberList(StringUtils.join(numberList,","))
                .build();
    }

    /**
     * 30天报表数据导出
     *
     * @param response
     */
    @Override
    public void export(HttpServletResponse response)  {
        //1、查出30天的报表所需数据
        //1.1、创建近三十天的时间集合
        LocalDate begin = LocalDate.now().minusDays(30);
        LocalDate end = LocalDate.now().minusDays(1);
        List<LocalDate> localDates = this.getTemporalInterval(begin, end);
        //2.2、30天内的[营业额，订单完成率，新增用户数，有效订单数，平均客单价]：需要数据：总金额、完成订单数和有效订单数、新增用户数
        //2.2.1、总金额
        Map map= new HashMap();
        map.put("begin",begin);
        map.put("end",end);
        map.put("status", Orders.COMPLETED);
        List<Map> moneyMapList = this.reportMapper.selectOrderNumbers(map);
        Map<String, BigDecimal> moneyMap = moneyMapList.stream().collect(Collectors.toMap(n -> (String) n.get("date"), n -> (BigDecimal) n.get("num")));
        //orderCountLists.stream().flatMapToLong(o -> LongStream.of(o)).sum();
        BigDecimal moneyCount = moneyMap.values().stream().reduce(BigDecimal::add).get();
        //2.2.2、 有效订单
        List<Map> validOrderCountList = this.reportMapper.getOrdersCount(map);
        //2.2.3、所有订单
        map.put("status",null);
        List<Map> orderCountList = this.reportMapper.getOrdersCount(map);
        //2.2.3、 将集合转为Map
        Map<String, Long> validOrderMap = validOrderCountList.stream().collect(Collectors.toMap(n -> (String) n.get("date"), n -> (Long) n.get("num")));
        Long validOrderCount = validOrderMap.values().stream().reduce(Long::sum).get();
        Map<String, Long> orderMap = orderCountList.stream().collect(Collectors.toMap(n -> (String) n.get("date"), n -> (Long) n.get("num")));
        Long orderCount = orderMap.values().stream().reduce(Long::sum).get();
        //2.2.4、新增用户
        List<Map> userNumMapList =  this.reportMapper.groupAddUserNum(begin,end);
        Map<String, Long> userNumMap = userNumMapList.stream().collect(Collectors.toMap(u -> (String) u.get("date"), u -> (Long) u.get("num")));
        Long userCount = userNumMap.values().stream().reduce(Long::sum).get();
        XSSFWorkbook workbook = null;
        ServletOutputStream outputStream = null;
        InputStream in = null;
        try {
            //2、读取本地的excel模板
//        InputStream in = this.getClass().getClassLoader().getResourceAsStream("运营数据报表模板.xlsx");
            in = this.getClass().getClassLoader().getResourceAsStream(URLDecoder.decode("temp/运营数据报表模板.xlsx", "utf-8"));
            //3、将本地数据写入excel模板

            workbook = new XSSFWorkbook(in);
            //3.1、根据excel文件对象拿到Sheet页
            XSSFSheet sheet = workbook.getSheetAt(0);
            //3.2、根据Sheet拿到Row行，通过Row拿到Cell列，开始遍历
            // 1列1行
            XSSFRow row2 = sheet.getRow(1);
            //设置时间
            row2.getCell(1).setCellValue("开始时间：" + begin + "结束时间：" + end);

            //3行 2、4、6列
            XSSFRow row4 = sheet.getRow(3);
            row4.getCell(2).setCellValue(moneyCount.doubleValue());
            Double orderCompletionRate = orderCount == 0 ? 0.0 : (double) validOrderCount / orderCount;
            row4.getCell(4).setCellValue(orderCompletionRate);
            row4.getCell(6).setCellValue(userCount);

            //4行 2、4列
            XSSFRow row5 = sheet.getRow(4);
            row5.getCell(2).setCellValue(validOrderCount);
            row5.getCell(4).setCellValue(moneyCount.doubleValue() / validOrderCount);

            //7行 1,2,3,4,5,6列
            for (int i = 0; i < localDates.size() ; i++) {
                XSSFRow row8 = sheet.getRow(7+i);
                LocalDate localDate = localDates.get(i);
                //1、列
                row8.getCell(1).setCellValue(localDate.toString());
                //2、列
                BigDecimal bigDecimal = moneyMap.get(localDate.toString());
                bigDecimal = bigDecimal == null ? new BigDecimal(0) : bigDecimal;
                row8.getCell(2).setCellValue(bigDecimal.doubleValue());
                //3、列
                Long num = validOrderMap.get(localDate.toString());
                num = num == null ? 0 : num;
                row8.getCell(3).setCellValue(num);
                //4、列
                Long validOrder = validOrderMap.get(localDate.toString());
                validOrder = validOrder != null ? validOrder : 0;
                //所有订单
                Long order = orderMap.get(localDate.toString());
                order = order != null ? order : 0;
                Double orderMoney = order == 0 ? 0.0 : (double) validOrder / order ;
                row8.getCell(4).setCellValue(orderMoney);
                //5、列
                double v = validOrder == 0.0 ? 0.0 : bigDecimal.doubleValue() / validOrder;
                row8.getCell(5).setCellValue(v);
                //6、列
                Long userNum = userNumMap.get(localDate.toString());
                userNum = userNum != null ? userNum : 0;
                row8.getCell(6).setCellValue(userNum);
            }

            //4、将excel写入到response中
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                workbook.close();
                outputStream.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过初试时间和截止时间获取区间
     * @param begin
     * @param end
     * @return
     */
    private List<LocalDate> getTemporalInterval(LocalDate begin, LocalDate end) {
        //使用while循环，条件：begin == end
        List<LocalDate> localDates = new ArrayList<>();
        localDates.add(begin);
        while (!begin.equals(end)){
            begin =  begin.plusDays(1);
            localDates.add(begin);
        }

        return localDates;
    }

    public static void main(String[] args) {
        LocalDate begin = LocalDate.now().minusDays(30);
        LocalDate end = LocalDate.now().minusDays(1);

        //使用while循环，条件：begin == end
        List<LocalDate> localDates = new ArrayList<>();
        localDates.add(begin);
        while (!begin.equals(end)){
            begin =  begin.plusDays(1);
            localDates.add(begin);
        }
        System.out.println(localDates.size());
    }
}
